package com.hyw.cm.server;

import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.hyw.cm.common.protocal.spi.IProtocalDriver;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.timeout.IdleStateHandler;

final class ProtocolDecoderRouter extends ByteToMessageDecoder {
  private static final Logger log = LoggerFactory.getLogger(ProtocolDecoderRouter.class);

  @Override
  protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

    IProtocalDriver driver = null;

    // find the target decoder
    ServiceLoader<IProtocalDriver> drivers = ServiceLoader.load(IProtocalDriver.class);
    for (IProtocalDriver dr : drivers) {
      if (Objects.nonNull(dr.getDecoder()) //
          && dr.getDecoder().getIdentifyLength() > 0 //
          && in.readableBytes() >= dr.getDecoder().getIdentifyLength()) {

        byte[] iden = new byte[dr.getDecoder().getIdentifyLength()];
        in.markReaderIndex();
        in.readBytes(iden);
        in.resetReaderIndex();

        if (dr.getDecoder().isSupport(iden)) {
          Assert.isTrue(Objects.isNull(driver),
              String.format("业务解码器匹配异常,当前当前连接将中断.channel Id:%s,Msg:%s", ctx.channel().id(), ByteBufUtil.hexDump(in)));
          driver = dr;
        }

      }
    } // End of driver selection

    if (Objects.nonNull(driver)) {
      // add idle handle by specified
      ctx.pipeline().removeFirst();
      ctx.pipeline().addFirst(String.format("%s#IdleHandler", driver.getName()),
          new IdleStateHandler(driver.getReadTimeout().getSeconds(), driver.getWriteTimeout().getSeconds(),
              driver.getAllTimeout().getSeconds(), TimeUnit.SECONDS));

      // add expected decoder into pineline
      ctx.pipeline().replace(this, driver.getDecoder().getName(), (ChannelHandler) driver.getDecoder().create());
      log.info("add decoder for {} ", driver.getDecoder().getName());
    } else {
      log.warn("Illegal Protocal Access:{}", ByteBufUtil.hexDump(in).toUpperCase());
      ctx.channel().close();
    } // end of driver binding
  }

}